const requiredReg = /required/i
const containsReg = /\D*\:\d*,?/i
const typeReg = /array|string|number/i
const numberReg = /\d*(.)?\d+/g

const filterSpecialReg = /['\*\,\.<>;]|\s+or|or\s+/gi

function filterStr (val) {
  return val.replace(filterSpecialReg, '');
}

const defaultMsg = {
  'required': function (field, value) {
    return field + ' is required';
  },
  'type': function (field, value) {
    return field + ' Field type error';
  },
  'min': function (field, value) {
    return 'The length must be at least ' + value;
  },
  'max': function (field, value) {
    return 'The length cannot exceed '+ value +' characters';
  },
  'in': function (field, value) {
    return 'Must contain ' + value;
  },
  'between': function (field, value) {
    return 'It\'s not between ' + value;
  },
  'not_in': function (field, value) {
    return 'Must not include ' + value;
  },
}

const contains = {
  'min': minFun,
  'max': maxFun,
  'in': inFun,
  'between': betweenFun,
  'not_in': notInFun,
  'required': isRequired,
  'type': isType
}

function minFun (str, len) {
  return String(str).replace(/^\s+|\s+$/g, '').length >= len;
}

function maxFun (str, len) {
  return String(str).replace(/^\s+|\s+$/g, '').length <= len;
}

function inFun (val, inArr) {
  return inArr.split(',').includes(String(val));
}

function betweenFun (val, inArr) {
  return val >= inArr.split(',')[0] && val <= inArr.split(',')[1];
}

function notInFun (val, inArr) {
  return !inArr.split(',').includes(String(val));
}

function isRequired (data) {
  return (data === undefined || data === '') ? false : true;
}

function isType (data, type) {
  if (['string', 'number'].includes(type)) {
    // 字符串
    if (!Number(data) && data !== 0) {
      return type == 'string';
    } else {
      // console.log('data', data, type == 'number')
      // return numberReg.test(data);
      return type == 'number';
    }
  }
  return getType(data) == type;
}

function getType(value) {
  if (value === null) {
    return value + "";
  }
  if (typeof value === "object") {
    // 数组、对象、null 用 typeof 都是 object，所以需要处理下 以 {} 为例
    let valueClass = Object.prototype.toString.call(value) // 转成这样 [object, Object]
    let type = valueClass.replace(/\[object\s+(\D*)\]/g, '$1');
    return type.toLowerCase();
  } else {
    return typeof value;
  }
}

// 判断是否是数据类型
function isContains (reg, data) {
  let inArr = reg.split(':');
  let [ ruleKey, ruleVal ] = inArr;
  let s = false;
  if (typeReg.test(ruleKey)) {
    s = isType(data, ruleKey);
    ruleKey = 'type';
  } else {
    s = contains[ruleKey](data, ruleVal);
  }
  
  return {
    field: s ? '' : ruleKey,
    status: s,
    value: s ? '' : ruleVal ? ruleVal : null
  };
}


/**
 * 判断是否必填
 * @param {*} data 10
 * @param {*} rules 'required|in:10,18',
 * @returns 
 */
function valid (data, rules) {
  let type = null;
  let value = null;
  const rulesArr = rules.split('|');
  const valid = rulesArr.every((rule) => {
    let res = isContains(rule, data);
    if (!res.status) {
      type = res.field;
      value = res.value;
    }
    // console.log('res', res)
    // console.log(data)
    // 存在数据则判断规则，不存在数据则判断是否必填
    return data !== '' ? res.status : !requiredReg.test(rules)
  })

  return {
    type,
    value,
    valid
  };
}


/**
 * 入参检测
 * @param {*} data 
 * @param {*} rules 
 * {
      'age': 'required|in:10,18',
      'uid': 'required|number|min:2|max:5',
      'filter': 'in:1,2,3',
      'category': 'number'
    }
 * @param {*} message 
 * @returns {field, type, msg} 判断通过则field为空
 */
function validate (data, rules, message) {
  let type = null;
  let msg = null;
  let field = null;

  for (let key in rules) {
    const validData = valid(data[key], rules[key]);
    if (!validData.valid) {
      type = validData.type;
      field = key;
      msg = defaultMsg[type](field, validData.value);
      break;
    }
  }

  for (let i in message) {
    if (
      i.split('.')[0] == field &&
      i.split('.')[1] == type
    ) {
      msg = message[i];
      break;
    }
  }
  return {
    field,
    type,
    msg
  };
}

class Validate {
  constructor(params, rules, message) {
    this.type = null;
    this.msg = null;
    this.field = null;
    this.params = params;
    this.rules = rules;
    this.message = message;
    this.contains = new Map();

    this.init();
  }

  init () {
    this.setContains('min', minFun)
    this.setContains('max', maxFun)
    this.setContains('in', inFun)
    this.setContains('between', betweenFun)
    this.setContains('not_in', notInFun)
    this.setContains('required', isRequired)
    this.setContains('type', isType)
  }

  // 验证
  validation (rules) {
    for (let key in rules) {
      let {valid, type, value} = this.valid(data[key], rules[key]);
      if (!valid) {
        this.type = type;// in, min ,not_in等类型
        this.field = key;// 字段名
        this.msg = defaultMsg[type](key, value);
        break;
      }
    }

    // 自定义message
    for (let i in message) {
      if (
        i.split('.')[0] == this.field &&
        i.split('.')[1] == this.type
      ) {
        msg = message[i];
        break;
      }
    }
  }

  valid (data, rules) {
    let type = null;
    let value = null;
    const rulesArr = rules.split('|');
    let valid = rulesArr.every((rule) => {
      let status = this.isContains(rule, data);
      if (!status.status) {
        type = status.field;
        value = status.value;
      }
      return status.status || !requiredReg.test(rules);
    })
  
    return {
      type,
      value,
      valid
    };
  }

  // 判断是否是数据类型判断
  isContains (reg, data) {
    let inArr = reg.split(':');// 例子：in:1,2
    let [ ruleKey, ruleVal ] = inArr;
    let s = false;
    if (typeReg.test(ruleKey)) {
      s = isType(data, ruleKey);
      ruleKey = 'type';
    } else {
      s = this.contains.get(ruleKey)(data, ruleVal);
    }
    
    return {
      field: s ? '' : ruleKey,
      status: s,
      value: s ? '' : ruleVal ? ruleVal : null
    };
  }

  setContains (key, fn) {
    this.contains.set(key, fn);
  }
}


/**
 * 
 * @param {string} rules 
 * @param {object} message 
 * rules: {
      'age': 'required|in:10,18',
      'uid': 'required|min:1',
      'filter': 'in:1,2,3',
      'category': 'number|not_in:1,2'
    }
  message: {
      'age.in': 'age必须在10，18之间',
      'uid.required': 'uid 是必须的',
      'uid.numeric': 'uid 必须是数字',
      'filter.in': 'filter 必须在(1,2,3)范围内',
      'category.number': 'category 必须为数组'
      'category.not_in': 'category 不允许在（1，2）范围内'
    }
 * @returns 
 */
const validateFun = (rules, message = {}) => {
  return async (ctx, next) => {
    let params = null;
    if (ctx.method == 'POST') {
      params = ctx.request.body;
    } else {
      params = ctx.request.query;
    }
    console.log('params', params)

    if (rules) {
      const { field, msg } = validate(params, rules, message);
      if (field) {
        ctx.utils.assert(false, ctx.utils.throwError(402, msg));
        // ctx.throw({
        //   code: 402,
        //   message: msg
        // })
      }
    }
    
    await next();
  }
}

module.exports = validateFun;